package net.linku.service.impl;

import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.nacos.client.naming.utils.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.linku.annotation.ChangeRecordEnums;
import net.linku.annotation.ChangeRecordLog;
import net.linku.annotation.change.service.ContentParser;
import net.linku.annotation.enums.ChangeSourceEnum;
import net.linku.annotation.vo.ChangeRecordBizVO;
import net.linku.converter.ChangeRecordMapperConverter;
import net.linku.domain.LAChangeRecord;
import net.linku.repository.LAChangeRecordRepository;
import net.linku.service.LAAccountService;
import net.linku.utils.JsonUtil;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

/**
 * 变更记录业务服务类。
 * <p>
 * 提供对变更记录的处理和管理，包括插入、删除、修改操作记录的保存，批量保存变更记录，
 * 以及对变更记录进行查询的功能。主要用于记录系统中数据的变更情况，以便于后续的审计和追溯。
 * </p>
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class LAAccountServiceImpl implements LAAccountService {

    private final LAChangeRecordRepository changeRecordRepository;
    private final ContentParser defaultContentParse;

    /**
     * 保存新增操作记录。
     *
     * @param idList          业务ID列表
     * @param changeRecordLog 变更记录注解，包含操作描述等信息
     */
    public void saveInsertHistory(List<Long> idList, ChangeRecordLog changeRecordLog) {
        List<ChangeRecordBizVO> changeRecordDoList = new ArrayList<>();
        for (Long insertId : idList) {
            log.info("开始写入新增操作记录, id: {}, 操作描述: {}", insertId, changeRecordLog.desc());
            Object afterData = defaultContentParse.getResultById(insertId, changeRecordLog);
            log.info("写入新增操作记录, id: {}, 操作描述: {}, afterData: {}", insertId, changeRecordLog.desc(), JsonUtil.toJson(afterData));
            ChangeRecordBizVO changeRecordBizBO = convertChangeRecordDo(null, afterData, changeRecordLog);
            changeRecordDoList.add(changeRecordBizBO);
        }
        saveBatchChangeListHistory(changeRecordDoList);
        log.info("结束写入新增操作记录, id: {}, 操作描述: {}", JsonUtil.toJson(idList), changeRecordLog.desc());
    }

    /**
     * 删除动作
     *
     * @param idList          id列表
     * @param changeRecordLog 变更记录注解
     */
    public void saveDeleteHistory(Map<Object, Object> oldValueObjectMap, List<Long> idList, ChangeRecordLog changeRecordLog) {
        List<ChangeRecordBizVO> changeRecordDoList = new ArrayList<>();
        for (Long deleteId : idList) {
            log.info("开始写入删除操作记录, id: {}, 操作描述: {}", deleteId, changeRecordLog.desc());
            ChangeRecordBizVO changeRecordBizBO = convertChangeRecordDo(oldValueObjectMap, null, changeRecordLog);
            changeRecordDoList.add(changeRecordBizBO);
        }
        saveBatchChangeListHistory(changeRecordDoList);
        log.info("结束写入删除操作记录, id: {}, 操作描述: {}", JsonUtil.toJson(idList), changeRecordLog.desc());
    }

    /**
     * 修改动作
     *
     * @param oldValueObjectMap 变更前数据
     * @param idList            变更id
     * @param changeRecordLog   操作记录注解
     */
    public void modifySaveHistory(Map<Object, Object> oldValueObjectMap, List<Long> idList, ChangeRecordLog changeRecordLog) {
        List<ChangeRecordBizVO> changeRecordDoList = new ArrayList<>();
        for (Long modifyId : idList) {
            log.info("开始写入修改操作记录, id: {}, 操作描述: {}", modifyId, changeRecordLog.desc());
            Object afterData = defaultContentParse.getResultById(modifyId, changeRecordLog);
            log.info("写入修改操作记录, id: {}, 操作描述: {}, afterData: {}", modifyId, changeRecordLog.desc(), JsonUtil.toJson(afterData));
            ChangeRecordBizVO changeRecordBizBO = convertChangeRecordDo(oldValueObjectMap, afterData, changeRecordLog);
            changeRecordDoList.add(changeRecordBizBO);
        }
        saveBatchChangeListHistory(changeRecordDoList);
        log.info("结束写入修改操作记录, id: {}, 操作描述: {}", JsonUtil.toJson(idList), changeRecordLog.desc());
    }

    /**
     * 批量保存
     *
     * @param changeRecordDoList 批量保存对象
     */
    public void saveBatchChangeListHistory(List<ChangeRecordBizVO> changeRecordDoList) {
        if (CollectionUtils.isEmpty(changeRecordDoList)) {
            return;
        }
        List<LAChangeRecord> changeRecordDOList = ChangeRecordMapperConverter.INSTANCE.convertDo(changeRecordDoList);
        changeRecordDOList.forEach(item -> bindOtherInfo(item));

        //批量写入
        boolean result = changeRecordRepository.insertBatch(changeRecordDOList);

        log.info("保存历史记录批量写入:{}", result);
    }


    /**
     * 设置其他信息
     *
     * @param changeRecordDO 数据库对象
     */
    private void bindOtherInfo(LAChangeRecord changeRecordDO) {
        bindChangeDesc(changeRecordDO);
        bindChangeSource(changeRecordDO);
    }

    private void bindChangeDesc(LAChangeRecord changeRecord) {
        String changeDesc = changeRecord.getChangeDesc();
        if (StringUtils.isBlank(changeDesc)) {
            ChangeRecordEnums.ACTION action = ChangeRecordEnums.ACTION.matchTypeEnum(changeRecord.getAction());
            changeDesc = Optional.ofNullable(action).map(ChangeRecordEnums.ACTION::getDesc).orElse("");
            changeRecord.setChangeDesc(changeDesc);
        }
    }

    private void bindChangeSource(LAChangeRecord changeRecord) {
        String changeSource = "linku_admin";
        if (StrUtil.isEmpty(changeSource)) {
            changeRecord.setChangeResource("");
            return;
        }
        changeRecord.setChangeResource(ChangeSourceEnum.matchCodeEnum(changeSource).getCode());
    }

    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
    public Long insert(LAChangeRecord changeRecord) {
        changeRecordRepository.save(changeRecord);
        Long incrementId = changeRecord.getId();
        log.info("记录变更记录,businessId: {}, businessType : {} , incrementId : {}", changeRecord.getBusinessId(), changeRecord.getBusinessType(), incrementId);
        return incrementId;
    }


    /**
     * 变更记录对象
     *
     * @param beforeObjectMap 更改记录
     * @param changeRecordLog 注解
     * @return 转换后的变更记录对象
     */
    private ChangeRecordBizVO<Object> convertChangeRecordDo(Map<Object, Object> beforeObjectMap, Object afterObj, ChangeRecordLog changeRecordLog) {
        ChangeRecordEnums.TYPE businessType = changeRecordLog.businessType();
        Object id = ReflectUtil.getFieldValue(afterObj, changeRecordLog.keyId());
        Object beforeObj = Optional.ofNullable(beforeObjectMap).orElse(Collections.emptyMap()).get(id);
        return ChangeRecordBizVO.builder()
                .businessId(String.valueOf(id))
                .businessTypeEnums(businessType)
                .actionEnums(changeRecordLog.action())
                .beforeData(beforeObj)
                .afterData(afterObj)
                .changeType(changeRecordLog.changeType())
                .changeDesc(changeRecordLog.desc())
                .build();
    }

    public Page<LAChangeRecord> queryListByCondition(LAChangeRecord changeRecord, Integer pageNum, Integer pageSize) {
        return changeRecordRepository.queryListByCondition(changeRecord, pageSize, pageNum);
    }

    private boolean isList(Object obj) {
        return (obj instanceof List<?>);
    }
}
